package com.ls.spring.framework.beans.support;

import com.ls.spring.framework.annotation.MyComponent;
import com.ls.spring.framework.annotation.MyController;
import com.ls.spring.framework.annotation.MyService;
import com.ls.spring.framework.beans.config.MyBeanDefinition;
import org.apache.commons.lang3.StringUtils;

import java.io.File;
import java.io.InputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;

/**
 * @author 挥之以墨
 */
public class MyBeanDefinitionReader {

    /**
     * 配置文件
     */
    private Properties contextConfig = new Properties();
    /**
     * 扫描的类全路径
     */
    private List<String> registryBeanClasses = new ArrayList<>();

    public MyBeanDefinitionReader(String[] configLocations) {
        doConfig(configLocations[0]);
        // 扫描配置路径中的类，放入类全路径列表中
        doScanner(contextConfig.getProperty("scanPackage"));
    }

    /**
     * 获取properties内容
     *
     * @param configLocation 配置文件路径
     */
    private void doConfig(String configLocation) {
        String configPath = configLocation.replaceAll("classpath:", "");
        try (InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream(configPath)) {
            contextConfig.load(inputStream);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 扫描路径中的类
     *
     * @param scanPackage 扫描路径
     */
    private void doScanner(String scanPackage) {
        // 将jar包路径转为文件路径
        URL url = this.getClass().getClassLoader().getResource("/" + scanPackage.replaceAll("\\.", "/"));
        if (url == null) {
            return;
        }
        // 递归扫描以.class结尾的文件
        File[] files = new File(url.getFile()).listFiles();
        if (files == null) {
            return;
        }
        for (File file : files) {
            String fileName = file.getName();
            if (file.isDirectory()) {
                doScanner(scanPackage + "." + fileName);
            } else if (fileName.endsWith(".class")) {
                // 全类名 = 包名.类名
                String className = scanPackage + "." + fileName.replace(".class", "");
                registryBeanClasses.add(className);
            }
        }
    }

    /**
     * 根据全类名获取class文件，封装成BeanDefinition
     *
     * @return BeanDefinition列表
     */
    public List<MyBeanDefinition> loadBeanDefinitions() {
        List<MyBeanDefinition> resultList = new ArrayList<>();

        try {
            for (String className : registryBeanClasses) {
                Class<?> clazz = Class.forName(className);
                // 检查是否有spring注解
                if (!clazz.isAnnotationPresent(MyController.class) && !clazz.isAnnotationPresent(MyService.class)) {
                    continue;
                }
                // 1.如果实现了接口，每一个接口只有一个实现类(此版本未实现Qualifier）
                for (Class<?> i : clazz.getInterfaces()) {
                    resultList.add(doCreateBeanDefinition(i.getName(), className));
                }

                // 2.如果是service有自定义命名，则使用自定义命名
                if (clazz.isAnnotationPresent(MyService.class)) {
                    String beanName = clazz.getAnnotation(MyService.class).value();
                    if (StringUtils.isNotBlank(beanName)) {
                        resultList.add(doCreateBeanDefinition(toLowerFirstCase(beanName), className));
                        continue;
                    }
                }

                // 3.需要保存首字母小写的beanName和全类名
                resultList.add(doCreateBeanDefinition(toLowerFirstCase(clazz.getSimpleName()), className));

            }
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        return resultList;
    }

    /**
     * 创建beanDefinition
     *
     * @param beanName      首字母小写的beanName
     * @param beanClassName 全类名
     */
    private MyBeanDefinition doCreateBeanDefinition(String beanName, String beanClassName) {
        return new MyBeanDefinition(beanName, beanClassName);
    }

    /**
     * 将首字母转为小写，适用于纯英文
     *
     * @param simpleName 类名
     */
    public static String toLowerFirstCase(String simpleName) {
        int asca = 65, ascz = 90;
        char[] chars = simpleName.toCharArray();
        if (chars[0] >= asca && chars[0] <= ascz) {
            chars[0] += 32;
        }
        return String.valueOf(chars);
    }

    public Properties getConfig() {
        return this.contextConfig;
    }
}